package ch.sv7.tool.dbmanager.db.xml;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import ch.sv7.tool.dbmanager.db.AbstractDatabaseSchemaFactory;
import ch.sv7.tool.dbmanager.db.ColumnSchema;
import ch.sv7.tool.dbmanager.db.ColumnType;
import ch.sv7.tool.dbmanager.db.ConstraintSchema;
import ch.sv7.tool.dbmanager.db.ForeignKeySchema;
import ch.sv7.tool.dbmanager.db.IndexSchema;
import ch.sv7.tool.dbmanager.db.TableSchema;

public class XmlDatabaseSchemaFactory extends AbstractDatabaseSchemaFactory {

	private HashMap tables;

	public XmlDatabaseSchemaFactory(InputStream stream, String schema) {
		tables = new HashMap();
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = null;
		try {
			builder = factory.newDocumentBuilder();
		} catch (ParserConfigurationException pce) {
			System.out.println(pce.getMessage());
			return;
		}
		try {
			Document document = builder.parse(stream);
			Element rootElement = document.getDocumentElement();
			NodeList nodes = rootElement.getElementsByTagName("table");
			for (int i = 0; i < nodes.getLength(); i++) {
				Element tableNode = (Element) nodes.item(i);
				String tableName = tableNode.getAttribute("name");
				TableSchema tableSchema = new TableSchema(tableName, schema);
				tables.put(tableName, tableSchema);
				NodeList fieldNodes = tableNode.getElementsByTagName("column");
				for (int j = 0; j < fieldNodes.getLength(); j++) {
					Element fieldNode = (Element) fieldNodes.item(j);
					String fieldName = fieldNode.getAttribute("name");
					String typeStr = fieldNode.getAttribute("type");
					String sizeStr = fieldNode.getAttribute("size");
					String decimalSizeStr = fieldNode.getAttribute("decimal-size");
					String nullableStr = fieldNode.getAttribute("nullable");
					boolean primaryKey = "TRUE".equalsIgnoreCase(fieldNode.getAttribute("key"));				
					String typeName = null;
					if (fieldNode.hasAttribute("type-name")){
						typeName = fieldNode.getAttribute("type-name");
					}
					String defaultValue = null;
					if (fieldNode.hasAttribute("default-value")){
						defaultValue = fieldNode.getAttribute("default-value");
					}
					boolean autoIncrement = false;
					if (fieldNode.hasAttribute("auto-increment")){
						autoIncrement = Boolean.valueOf(fieldNode.getAttribute("auto-increment")).booleanValue();
					}
					if ("NULL".equals(defaultValue)){
						defaultValue = null;
					}
					ColumnSchema column = new ColumnSchema(
							tableSchema, 
							fieldName, 
							getDataType(typeStr, sizeStr, decimalSizeStr, nullableStr, autoIncrement, typeName, defaultValue), 
							primaryKey);
					tableSchema.addColumnSchema(column);
				}
				NodeList indexNodes = tableNode.getElementsByTagName("index");
				for (int j = 0; j < indexNodes.getLength(); j++) {
					Element indexNode = (Element) indexNodes.item(j);
					String columns = indexNode.getAttribute("columns");
					String qualifier = indexNode.getAttribute("qualifier");
					String type = indexNode.getAttribute("type");
					String name = indexNode.getAttribute("name");
					boolean unique = "true".equalsIgnoreCase(indexNode.getAttribute("unique"));
					boolean primaryKey = "true".equalsIgnoreCase(indexNode.getAttribute("primary-key"));
					IndexSchema index = new IndexSchema(tableSchema, name, unique, qualifier, type, columns, primaryKey);
					tableSchema.getIndexes().add(index);
				}	
				
				NodeList fkNodes = tableNode.getElementsByTagName("foreign-key");
				for (int j = 0; j < fkNodes.getLength(); j++) {
					Element fkNode = (Element) fkNodes.item(j);
					String name = fkNode.getAttribute("name");
					String column = fkNode.getAttribute("columns");
					String fTable = fkNode.getAttribute("foreign-table");
					String fColumn = fkNode.getAttribute("foreign-columns");
					String updateRule = fkNode.getAttribute("update-rule");
					String deleteRule = fkNode.getAttribute("delete-rule");
					String deferrability = fkNode.getAttribute("deferrability");
					ForeignKeySchema fk = new ForeignKeySchema(tableSchema, name, 
							column, 
							fTable,
							fColumn,
							updateRule,
							deleteRule,
							deferrability);
					tableSchema.getForeignKeys().add(fk);
				}	
				
				NodeList csNodes = tableNode.getElementsByTagName("constraint");
				for (int j = 0; j < csNodes.getLength(); j++) {
					Element csNode = (Element) csNodes.item(j);
					String name = csNode.getAttribute("name");
					String defintion = csNode.getAttribute("definition");
					ConstraintSchema cs = new ConstraintSchema(tableSchema, name, defintion);
					tableSchema.getConstraints().add(cs);
				}

			}

		} catch (FileNotFoundException e) {
			System.out.println(e.getMessage());
			return;
		} catch (SAXException e) {
			System.out.println(e.getMessage());
			return;
		} catch (IOException e) {
			System.out.println(e.getMessage());
			return;
		}
	}

	public XmlDatabaseSchemaFactory(String filename, String schema) throws FileNotFoundException {
		this(((new FileInputStream(filename))), schema);
	}

	public TableSchema getTableSchema(String tableName) {
		return (TableSchema) tables.get(tableName);
	}

	public static ColumnType getDataType(String typeStr, String sizeStr, String decimalSizeStr, String nullableStr, boolean autoIncrement, String typeName, String defaultValue) {
		int type = ColumnType.stringToJdbcType(typeStr);
		long size = Long.parseLong(sizeStr);
		long decimalSize = 0L;
		if (decimalSizeStr != null && decimalSizeStr.trim().length() > 0)
			decimalSize = Long.parseLong(decimalSizeStr);
		boolean nullable = "true".equalsIgnoreCase(nullableStr);
		ColumnType colType = new ColumnType(type, size, decimalSize, nullable, autoIncrement, typeName, defaultValue);
		return colType;
	}

	public String[] getTables() {
		return (String[])tables.keySet().toArray(new String[tables.size()]);
	}
	
	
}
